package net.techwatch.poi;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.apache.poi.hssf.usermodel.HSSFCell;
import org.apache.poi.hssf.usermodel.HSSFDateUtil;
import org.apache.poi.hssf.usermodel.HSSFRow;
import org.apache.poi.hssf.usermodel.HSSFSheet;

public class XlsReader<T> implements RowFileReader<T> {

	private HSSFSheet sheet;
	private Class<T> clazz;
	private Matcher<T> matcher;
	private List<String> properties = new ArrayList<String>();
	private int currentIndex = 0;

	/**
	 * 
	 */
	private XlsReader() {
		// nothing to do
	}

	/**
	 * @param <T>
	 * @param clazz
	 * @param matcher
	 * @param sheet
	 * @return
	 */
	public static <T> XlsReader<T> createXlsReader(Class<T> clazz,
			Matcher<T> matcher, HSSFSheet sheet) {
		XlsReader<T> reader = new XlsReader<T>();
		reader.clazz = clazz;
		reader.matcher = matcher;
		reader.sheet = sheet;

		return reader;
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#hasNext()
	 */
	public boolean hasNext() {
		return sheet.getLastRowNum() > currentIndex;
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#next()
	 */
	public T next() {
		HSSFRow row = null;
		do {
			currentIndex++;
			row = sheet.getRow(currentIndex);
		} while(row == null && currentIndex < sheet.getLastRowNum());
		return processRow(row);
	}

	/* (non-Javadoc)
	 * @see java.util.Iterator#remove()
	 */
	public void remove() {
		throw new UnsupportedOperationException();
	}

	/* (non-Javadoc)
	 * @see poi.RowFileReader#reset()
	 */
	public void reset() {
		this.properties = Arrays.asList(matcher.matchableProperties());
		for(int rowIndex = sheet.getFirstRowNum(); rowIndex < sheet.getLastRowNum(); rowIndex++) {
			HSSFRow row = sheet.getRow(rowIndex);
			// in case of empty row
			if(row == null)
				continue;
			
			boolean found = false;
			for(int cellIndex = row.getFirstCellNum(); cellIndex < row.getLastCellNum(); cellIndex++) {
				HSSFCell cell = row.getCell(cellIndex);
				if(cell.getCellType() == HSSFCell.CELL_TYPE_STRING) {
					for(int propertyIndex = 0; propertyIndex < properties.size(); propertyIndex++) {
						String property = properties.get(propertyIndex);
						// in case something match, sort the list from XLS file
						if(property.equals(cell.getRichStringCellValue().getString())) {
							if(propertyIndex != cellIndex - row.getFirstCellNum()) {
								String replacement = properties.get(cellIndex - row.getFirstCellNum());
								properties.set(cellIndex - row.getFirstCellNum(), property);
								properties.set(propertyIndex, replacement);
							}
							found = true;
							break;
						}
					}
					// if not found: go to the next row
					if(!found) break;
				}
			} // cell
			
			if(found) {
				this.currentIndex = rowIndex;
				break;
			}
		} // row
	
	}
	
	/**
	 * @param row
	 * @return
	 */
	private T processRow(HSSFRow row) {
		try {
			T o = clazz.newInstance();
			for (int i = row.getFirstCellNum(); i < row.getLastCellNum(); i++) {
				if(i - row.getFirstCellNum() >= properties.size())
					break;
				
				processCell(o, properties.get(i - row.getFirstCellNum()), row
						.getCell(i));
			}

			return o;
		} catch (InstantiationException e) {
			throw new IllegalStateException(e);
		} catch (IllegalAccessException e) {
			throw new IllegalStateException(e);
		}
	}

	/**
	 * @param o
	 * @param property
	 * @param cell
	 * @return
	 */
	private T processCell(T o, String property, HSSFCell cell) {
		switch (cell.getCellType()) {
		case HSSFCell.CELL_TYPE_STRING:
			matcher.match(o, property, cell.getRichStringCellValue()
					.getString());
			break;
		case HSSFCell.CELL_TYPE_NUMERIC:
			if (HSSFDateUtil.isCellDateFormatted(cell)) {
				matcher.match(o, property, cell.getDateCellValue());
			} else {
				matcher.match(o, property, cell.getNumericCellValue());
			}
			break;
		}

		return o;
	}

}
